%
% @brief 常用射频相关的工具类
% @author 41204@qq.com / 2025-09-29
% @note 本类中的函数均以 Static 方式定义，以方便使用。
%

classdef rfutils
    methods(Static)
        %% 加载 IQ 数据文件
        % @author 41204@qq.com / 2025-09-29
        % @param filepath [string] 数据文件路径名
        % @param type [string] 数据文件中 I/Q 数据类型名, 如: 'int16', 'float32'
        % @param offset [uint32] 数据起始偏移地址(字符数), 默认值为 0
        % @param samples [uint32] 数据点数, 默认值为到文件结束的所有点
        % @param endian [string](可选参数，默认值为 'ieee-le') 数据文件中 I/Q 数据类型的大小端, 如: 'ieee-le'
        % @return iq, info
        % @retval iq [1xn complex double] IQ 数据
        % @retval info [struct] 数据文件的相关信息
        function [iq, info] = load_iq(filepath, type, offset, samples, endian)
            if nargin < 3                
                offset = 0; % offset 默认值
            end
            
            if nargin < 4
                samples = Inf; % samples 默认值
            end
            
            if nargin < 5
                endian = 'ieee-le'; % endian 默认值
            end
            
            % 打开文件
            fid = fopen(filepath, 'rb');
            if fid == -1
                error('Open file failed: %s', filename);
            end

            % 跳转到偏移地址（文件开头为基准）
            fseek(fid, offset, 'bof');
            % 读取全部数据
            raw = fread(fid, [2, samples], type, 0, endian);
            % 关闭文件
            fclose(fid);
            % raw(1,:) 是 I，raw(2,:) 是 Q
            iq = raw(1,:) + 1i * raw(2,:);
            
            % 填写数据文件的相关信息
            info = struct();
            info.COMMENT = filepath;
            info.SAMPLES = length(iq);
        end

        %% 加载 RadioMaster 公司自定义的 ARB 数据文件
        % @author 41204@qq.com / 2025-09-29
        % @param filepath [string] 数据文件路径名
        % @return iq, info
        % @retval iq [1xn complex double] IQ 数据
        % @retval info [struct] ARB 数据文件的相关信息
        function [iq, info] = load_rmarb(filepath)        
            % 打开文件
            fid = fopen(filepath, 'rb');
            if fid == -1
                error('Open file failed: %s', filename);
            end
            
            % 读取并解析 16KB 固定长度的文件头字符串
            info = fread(fid, 16*1024, 'uint8=>char'); % 读取 16KB，并转为字符
            info = char(info');                        % 转为字符串（行向量）
            info = rm.utils.parse_kvargs(info, ';', '=');
            info.SAMPLES = str2double(info.SAMPLES);
            info.FREQUENCYSAMPLING = str2double(info.FREQUENCYSAMPLING);
            info.SIGNALBW = str2double(info.SIGNALBW);
            info.RMS = str2double(info.RMS);
            info.PAR = str2double(info.PAR);
            
            % 读取全部数据
            raw = fread(fid, [2, info.SAMPLES], 'int16', 0, 'ieee-le');
            % 关闭文件
            fclose(fid);
            
            % raw(1,:) 是 I，raw(2,:) 是 Q
            iq = raw(1,:) + 1i * raw(2,:);
        end

        %% 加载 R&S 公司的 WV 数据文件
        % @author 41204@qq.com / 2025-09-29
        % @param filepath [string] 数据文件路径名
        % @return iq, info
        % @retval iq [1xn complex double] IQ 数据
        % @retval info [struct] WV 数据文件的相关信息
        function [iq, info] = load_rswv(filepath)        
            % 打开文件
            fid = fopen(filepath, 'rb');
            if fid == -1
                error('Open file failed: %s', filename);
            end

            % 读取文件头
            % {TYPE: SMU-WV, 0}{COMMENT: BW200M.wv}{COPYRIGHT: RadioMaster}
            % {ORIGIN INFO: RS Matlab Toolkit}{LEVEL OFFS: 23.0092, 0}
            % {DATE: 2025-10-13;22:18:48}{CLOCK: 600000000}{SAMPLES: 6000000}
            % {CONTROL LENGTH: 6000000}{MARKER LIST 1: 0:1;1:0}
            % {WAVEFORM-24000001: #...(24000000个字节数据)...}
            info = '';
            while true
                % 读取一个字节并转为字符
                ch = fread(fid, 1, 'uint8=>char');

                % 替换字段分隔符
                if ch == '{'
                    continue;
                end
                if ch == '}'
                    ch = '$';
                end
                
                % 追加字符
                info = [info, ch];
                
                % 检查是否文件结束或遇到'#'
                if ch == '#'
                    break;
                end
            end
            
            % 解析各个字段
			info = rm.utils.parse_kvargs(info, '$', ':');
            info.SAMPLES = str2double(info.SAMPLES);
            info.CLOCK = str2double(info.CLOCK);
            
            % 读取全部数据
            raw = fread(fid, [2, info.SAMPLES], 'int16', 0, 'ieee-le');
            % 关闭文件
            fclose(fid);
            
            % raw(1,:) 是 I，raw(2,:) 是 Q
            iq = raw(1,:) + 1i * raw(2,:);
        end

        %% 重采样
        % @author 41204@qq.com / 2025-10
        % @param iq [1xn complex double] 原始 IQ 数据
        % @param s1 [double] 原始 IQ 采样率
        % @param s2 [double] 目标 IQ 采样率
        % @return iq [1xn complex double] 重采样后的 IQ 数据
        function iq_resampled = resample_iq(iq, s1, s2)
            % 计算重采样因子
            P = round(s2 / gcd(s1, s2));  % 上采样因子
            Q = round(s1 / gcd(s1, s2));  % 下采样因子
            
            % 重采样
            iq_resampled = resample(iq, P, Q);
        end
    end
end
